package org.axed.user.client;

import java.util.ArrayList;

import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;

import com.google.gwt.core.client.GWT;


/**
 * Holds and handels a single Line until a linebreak.
 */
public class Line {
	/**
	 * The document this line belongs to.
	 */
	Document doc;

	/**
	 * The htmlobject of this line.
	 */
	CursesElement curs;
	
	/**
 	 * Holds the text for the line.
 	 */
	SpaceString text;

	/**
	 * TODO
 	 */
	LineString lineStr;
	
	/**
 	 * True if this line has a terminating linebreak.
 	 */	
	boolean br = false;

	/**
	 * Constructor.
	 *
	 * @param doc   The document this line will belong to.
	 */
	public Line(Document doc) {
		this.doc = doc;
		text = new SpaceString();
		lineStr = new LineString(text.htmlText);
		curs = new CursesElement(text.htmlText);
		setFont(doc.getFont());
		setWrap(doc.getWrap());
		DOM.setStyleAttribute(curs.getElement(), "fontSize", doc.fontSize);
	}

	/**
	 * Constructor.
	 *
	 * @param doc   The document this line will belong to.
	 */
	public Line(Document doc, SpaceString text) {
		this.doc = doc;
		this.text = text;
		lineStr = new LineString(text.htmlText);
		curs = new CursesElement(text.htmlText);
		setFont(doc.getFont());
		setWrap(doc.getWrap());
		DOM.setStyleAttribute(curs.getElement(), "fontSize", doc.fontSize);
	}


	/**
	 * Constructor.
	 *
	 * Speedily create a line with text already in it.
	 * This one is e.g. used to paste stuff.
	 *
	 * @param doc   The document this line will belong to.
	 * @param text  The text this line will hold. 
	 */
	public Line(Document doc, String text) {
		this.doc = doc;
		this.text = new SpaceString(text);
		lineStr = new LineString(this.text.htmlText);
		curs = new CursesElement(this.text.htmlText);
		curs.setSuffix("<br/>");
		br = true;
		refresh();
	}


	/**
 	 * Adds a new char at the end of the line.
 	 */
	public void add(char c) {
		text.add(c);
		refresh();
	}

	/**
 	 * Adds a linebreak to this line.
 	 */
	public void addBreak() {
		if (br) {
			return;
		}
		curs.setSuffix("<br/>");
		br = true;
	}

	/**
	 * Adds an anchor from a range to this line.
	 */
	public void addAnchor(Anchor anchor, int col) {
		lineStr.insertAnchor(anchor, col);
	}

	/**
	 * Adds a range that spawns over this line. that means it has no anchors in it.
	 */
	public void addRange(Range range) {
		lineStr.addRange(range);
	}

	/**
	 * Re-sets the htmlText of anchor
	 */
	public void commitAnchor(Anchor anchor) {
		lineStr.commitAnchor(anchor);	
	}

	/**
	 * Returns true when range spawns over this line.
	 */
	public boolean hasRange(Range range) {
		return lineStr.hasRange(range);
	}

	/**
	 * Finds the column pos from a line for given top and left coordinates.
	 * Parameters start and forward give hints to get a faster solution.
	 *
	 * @param targetTop     the top coord to find.
	 * @param targetLeft    the left coord to find.
	 * 
	 * @param start         the column to start looking
	 * @param forward       true: looking forward, false: looking backward
	 */
	public int findColPos(int targetTop, int targetLeft, int start, boolean forward) {
		int cp = text.htmlText.colOf(curs.findEntryPos(targetTop, targetLeft, start, forward));
		return cp;
	}

	/**
	 * Finds the position of a sample 
	 *
	 * @return the position of the sample or -1 of not found.
	 */
	public int findStr(String sample) {
		String s = text();
		if (s == null) {
			return -1; 
		}
		return s.indexOf(sample);
	}

	
	/**
	 * Finds the position of a sample between col1 and col2.
	 *
	 * @return the position of the sample or -1 of not found.
	 */
	public int findStr(String sample, int col1, int col2) {
		String s = text();
		if (s == null) {
			return -1; 
		}
		int p = s.indexOf(sample, col1);
		if (p > col2) {
			return -1;
		}
		return p;
	}

	/**
	 * Finds the column pos from a line wrap.
	 *
	 * @param targetTop     the top coord to find.
	 * @param start         the column to start looking
	 * @param forward       true: looking forward, false: looking backward
	 */
	public int findWrap(int targetTop, int start, boolean forward) {
		int cp = text.htmlText.colOf(curs.findWrap(targetTop, start, forward));
		return cp;
	}
		
	/**
	 * Returns the coords of the column c.
	 */
	public void getCoords(int col, Coords answer) {
		int e = text.htmlText.entryOf(col);
		curs.getCoords(text.htmlText.entryOf(col), answer);
	}

	/**
	 * Returns the entry# of anchor a.
	 */
	public int getCol(Anchor a) {
		int e = getEntry(a);
		if (e < 0) {
			return -1;
		}
		return text.htmlText.colOf(e);
	}

	/**
	 * Returns the entry# of anchor a.
	 */
	public int getEntry(Anchor a) {
		return lineStr.getEntry(a);
	}

	/**
	 * Returns the line number of this line in the doc.
	 */
	public int getLineNum() {
		return doc.getLinePos(this);
	}

	/**
	 * Returns the CursesElement. 
	 */
	public CursesElement getCurses() {
		return curs;
	}

	/**
	 * Returns true if this line has a br terminator.
	 */
	public boolean hasBr() {
		return br;
	}

	/**
	 * Inserts a character at a sepecfic position.
	 *
	 * @param c    The character to insert.
	 * @param pos  The position to insert the character at.
	 */
	public void insert(char c, int pos) {
		text.insert(c, pos);
		refresh();
	}

	/**
	 * Inserts a String at a sepecfic position.
	 *
	 * @param str  The string to insert.
	 * @param pos  The position to insert the character at.
	 */
	public void insert(String s, int pos) {
		text.insert(s, pos);
		refresh();
	}


	/**
	 * Joins this line with another one.
	 *
	 * @return This line.
	 */
	public Line join (Line joinee) {
		if (!br) {
			// this should not happen (TODO throw exception);
			return null;
 		}
		text.join(joinee.text);
		br = joinee.br;
		refresh();
		return this;
	}

	private native int _search(String t, String r) /*-{
		return t.search(r);
	}-*/;

	/**
	 * Returns the first match of expr
	 */
	public int match(String expr) {
		String s = text();
		if (s == null) {
			return -1; 
		}
		return _search(s, expr);
	}

	/**
	 * Returns the first match of expr between col1 and col2
	 */
	public int match(String expr, int col1, int col2) {
		String s = text();
		if (s == null) {
			return -1; 
		}
		if (col1 > 0) {
			s = s.substring(col1 + 1, s.length());
		}
		int a = _search(s, expr);
		if (a < 0) {
			return -1;
		}
		a += col1 + (col1 > 0 ? 1 : 0);
		if (a > col2) {
			return -1;
		}
		return a;
	}

	/**
	 * Redraws the line.
	 */
	public void refresh() {
		curs.refresh();
	}

	/**
	 * Removes a range that spawns over this line. 
	 * That means it had no anchors in it.
	 */
	public void removeRange(Range range) {
		lineStr.removeRange(range);
	}

	/**
	 * Removes a character at pos.
	 *
	 * @param pos   The position of the character to remove.
	 * returns the char removed.
	 */
	public char remove(int pos) {
		char ch = text.charAt(pos);
		text.remove(pos);
		// send the changes to the browser.
		refresh();
		return ch;
	}

	/**
	 * Removes characters from start column to end coloumn.
	 *
	 * @param start     the column to start deletion from.
	 * @param end       the column to end deletion at.
	 */
	public String remove(int start, int end) {
		String ret = text.remove(start, end);
		refresh(); // send the changes to the browser.
		return ret;
	}

	/**
	 * removes an Anchor.
	 */
	public void removeAnchor(Anchor anchor) {
		lineStr.removeAnchor(anchor);
	}

	/**
	 * Removes the last characer.
	 */
	public void removeLast() {
		text.removeLast();
		// send the changes to the browser.
		refresh();
	}      

	/**
	 * Sets the primary font of this line
	 */
	public void setFont(int font) {
		switch (font) {
		case AxedArea.FONT_MONOSPACE:
			DOM.setStyleAttribute(curs.getElement(),  "fontFamily", "monospace");
			break;
		case AxedArea.FONT_SERIF:
			DOM.setStyleAttribute(curs.getElement(),  "fontFamily", "serif");
			break;
		}
		DOM.setStyleAttribute(curs.getElement(), "fontSize", doc.fontSize);
	}

	/**
	 * turns wrapping on/off for this line.
	 */
	public void setWrap(boolean wrap) {
		if (wrap) {
			DOM.setStyleAttribute(curs.getElement(), "whiteSpace", "normal");
		} else {
			DOM.setStyleAttribute(curs.getElement(), "whiteSpace", "nowrap");
		}
	}

	/**
	 * Returns the size of the line
	 */
	public int size() { 
		return text.length();
	}

	/**
	 * Returns the texts substring from b to e
	 */
	public String substring(int b, int e)
	{
		return text.text.substring(b, e);
	}

	/**
	 * Splits this line into two Lines.
	 *
	 * @param pos   The column to split the line at.
	 *
	 * @return The new line.
	 */
	public Line split(int pos) {
		Line nl = new Line(doc, text.split(pos));

		if (br) {
			nl.addBreak();
		} else {
			addBreak();
		}
		
		refresh();
		nl.refresh();
		return nl;
	}

	public String text()
	{
		return text.text.toString();
	}

//privates

}

